/**
 ******************************************************************************
 * @file app_pokemon.c
 * @author GX (2524913364@qq.com)
 * @brief   宝可梦APP
 *          (1)画面中的主要内容：会动的宝可梦，对话框，宝可梦的抱食度，喂食按键。
 *          (2)宝可梦有不同的状态，每个状态都会有不同的台词与玩家互动。
 *          (3)点击宝可梦可让宝可梦变成开心状态，并且会跳跃三下。
 *          (4)按住相对于宝可梦左边(右边)的区域宝可梦会朝着该位置移动，
 *             移动速度有三档，根据所在位置与目标位置的距离自动切换。
 *          (5)宝可梦移动和跳跃都会降低抱食度，当抱食度小于 20 时会变红色，当为 0
 *             时宝可梦不能再移动。必须喂食。
 *          (6)进入游戏和退出游戏都有动画，而且宝可梦也会打招呼。 
 * @version 1.0.0 
 * @date 2022-07-12
 * 
 * @copyright Copyright (c) 2022
 * 
 ******************************************************************************
 *----------------------------------------------------------------------------*
 *  Remark         : Description                                              *
 *----------------------------------------------------------------------------*
 *  Change History :                                                          *
 *  <Date>     | <Version> | <Author>       | <Description>                   *
 *----------------------------------------------------------------------------*
 *  2022-07-12 | 1.0.0     | GX             | Create file                     *
 *----------------------------------------------------------------------------*
 *                                                                            *
 ******************************************************************************
 */


/*----------------------------- start of file -------------------------------*/
#include "app_pokemon.h"
#include "lcd.h"
#include "touch_screen.h"


/*------------------------ Static function declaration ------------------------*/

static void opening_animation(int *fb_base, int fd, 
                                  struct fb_var_screeninfo *vinfo);
static void ending_animation(int *fb_base, int fd, 
                                  struct fb_var_screeninfo *vinfo);
static void refresh_interface(int *fb_base, int fd, 
                                  struct fb_var_screeninfo *vinfo);
static POKEMON_KEY pokemon_getKey(int x, int y);
static void check_State(void);
static void Init_pokemon(myPokemon *pokemon);


/*------------------------ Variable function declaration -----------------------*/

/* 初始化伊布宝可梦属性 */
static myPokemon Eevee = 
{ 
  false,                  /* 宝可梦没在说话（状态正在响应） */
  false,                  /* 宝可梦的状态还没响应 */
  310,                    /* 左上角的x坐标 */
  POKEMON_MOVE_UPLINE,    /* 左上角的y坐标 */
  100,                    /* 宽度 */
  85,                     /* 高度 */
  1,                      /* 当前贴纸 */
  14,                     /* 贴纸总數 */
  10,                     /* 抱食度 0～100 */
  POKEMON_MEET,           /* 状态 */
  POKEMON_NOTHING,        /* 正在响应的状态（未在响应） */
  -100,                   /* 最近一次检查状态的时间(一开始最好为负数，不然可能错过打招呼) */
  0,                      /* 开始说话(响应状态)的时间 */
  // time_cur             /* 当前时间要在外面初始化 */
  }; 

static bool start_game = true;
static bool end_game   = false;
static POKEMON_STATE responing = POKEMON_NOTHING;   /* 标记系统正在响应的某个状态 */

/* 记录开始时间，与后面使用的时间做差得到一个比较直观的时间量 */
static long time_start;    


/*******************************************************************************
 * 
 *                              宝可梦APP主要函数
 * 
*******************************************************************************/

bool app_Pokemon(void)
{
  printf("into pokemon\n");
  /*------------------------------开启显示屏幕-----------------------------*/
  int fd_lcd;                             /* 打开文件获取文件描述 */
  int *fb_base = NULL;                    /* 映射后显存的地址 */
  struct fb_var_screeninfo vinfo;         /* 显卡设备的可变属性结构体 */
	/* 打开文件 */
	fd_lcd = open(LCD_PATH, O_RDWR);
	if(fd_lcd == -1)
	{
		perror("open lcd");
    return false;
	}
  else
	{
		/* 显存映射 */
		fb_base = mmap(NULL, LCD_BUFF_SIZE*LCD_BLOCK,  PROT_READ|PROT_WRITE,
					MAP_SHARED, fd_lcd, 0);
		if(fb_base == MAP_FAILED) 
		{
			perror("申请显存失败");
      close(fd_lcd);
      return false;
		} 

    if(ioctl(fd_lcd, FBIOGET_VSCREENINFO, &vinfo) != 0)/* 获取可变属性 */
    {
        perror("获取LCD设备可变属性信息失败");
        return false;
    }
  }

  /*--------------------------------正式开始宝可梦相关-----------------------------*/
  /* 显示字符相关配置------------------------------- */
  /* 要先把进来前的字体配置记住，在结束后要设置回来 */
  bool useBack;
  u_int32_t TextColor, BackColor;
  sFONT *fonts;
  fonts = LCD_GetFont();
  LCD_GetColors(&TextColor, &BackColor, &useBack);
  LCD_SetFont(&POKEMON_FONT); 
  LCD_SetColors(POKEMON_TEXT_COLOR, POKEMON_BACK_COLOR, POKEMON_UNUES_BACKCOLOR);
  
  /* 跟宝可梦动作相关的变量-------------------- */
  int delta_x = 0;        /* 宝可梦所在位置与移动的目标位置的差值 */
  int move_distance = 0;  /* 实际需要宝可梦移动的距离 */
  char jump = 6;          /* 为6时不跳跃，小于6时偶数上跳，奇数下降 */

  /* 初始化宝可梦---------------------------- */
  Init_pokemon(&Eevee);

  /* 开场动画 */
  opening_animation(fb_base, fd_lcd, &vinfo);

  
  /* 更新状态机和按键相关---------------------- */
  Renew_tcState();
  TOUCH *tc = malloc(sizeof(TOUCH));
  POKEMON_KEY key = POKEMON_KEY_NONE;   /* 初始化键值 */

  /* 其他变量-------------------------------- */
  bool refresh = true;    /* 是否刷新显示的开关，为了实现每两次循环才刷新一次 */
  time_start = time(NULL);/* 获取开始时间 */  

  /* 进入循环----------------------------------------------- */
  while(1)
  {
    get_tcState(tc);
    if( !tc->response )
    {
      switch (tc->state)
      {
      case STATE_SINGLE_CLICK:
        response_over();
        key = pokemon_getKey(tc->x_last, tc->y_last);
        printf("key:%d\n", key);
        {
          switch (key)
          {
          case POKEMON_BACK:
            /* 宝可梦切换告别状态 */
            Eevee.state = POKEMON_GOODBYE;
            Eevee.state_responed = false;
            
            break;
          
          case POKEMON_KEY_FOOD:
            /* 宝可梦切换吃饭状态 */
            if( (Eevee.state != POKEMON_EATTING) && (Eevee.satiety < 100) )
            {
              Eevee.satiety = (Eevee.satiety <= 80) ? 
                                (Eevee.satiety+20) : 100;
              Eevee.state = POKEMON_EATTING;
              Eevee.state_responed = false;
            } 
            break;

          case MYPOKEMON_KEY:
            jump = 0;
            Eevee.state = POKEMON_HAPPY;
            Eevee.state_responed = false;
            
            break;

          default:
            break;
          }
        }
        break;

      case STATE_PRESSED:
        if( Eevee.satiety > 0)  /* 抱食度大于0 才能移动 */
        {
          delta_x = tc->x - Eevee.pos_x;
          printf("delta_x:%d\n", delta_x);
          if( delta_x < 0 )
          {/* 左移 */
          printf("move left\n");
          /* 判断宝可梦实际要移动的距离 */
            if (delta_x < -200)
              move_distance = -5;
            else if(delta_x < -100)
              move_distance = -3;
            else 
              move_distance = -1;

            Eevee.pos_x = (Eevee.pos_x > POKEMON_MOVE_LEFTLINE) ?
                            (Eevee.pos_x + move_distance) : POKEMON_MOVE_LEFTLINE;
          }
          else if( delta_x > 0 )
          {/* 右移 */
            printf("move right\n");
            /* 判断宝可梦实际要移动的距离 */
            if (delta_x > 200)
              move_distance = 5;
            else if(delta_x > 100)
              move_distance = 3;
            else 
              move_distance = 1;

            Eevee.pos_x = (Eevee.pos_x < POKEMON_MOVE_RIGHTLINE) ?
                            (Eevee.pos_x + move_distance) : POKEMON_MOVE_RIGHTLINE;
          }
          Eevee.de_satietyCount++;  /* 计数到10就减少抱食度 */
        }

        break;
      
      default:
        response_over();
        break;
      }
    }    

    /* 检查当前时间都否到时间检查状态---------------- */
    Eevee.time_cur = time(NULL) - time_start;
    if( ((Eevee.time_cur - Eevee.time_staCheck) >= POKEMON_STATE_Duration) &&
          (Eevee.responing != POKEMON_NOTHING))
    {/* 说话(响应)持续时间到，如果系统正在响应就终止响应 */
      Eevee.saying = false;
      Eevee.state_responed = true;
    
      /* 检查是否要切换新状态------- */
      if( Eevee.responing == POKEMON_GOODBYE )  /* 退出状态 */
      {
        break;
      }

      if( Eevee.satiety < POKEMON_HUNGRY )  /* 是否饥饿 */
      {
        Eevee.state = POKEMON_HUNGER;
        Eevee.state_responed = false;
      }
      else
      {
        Eevee.state = POKEMON_HAPPY;
      }
      Eevee.responing = POKEMON_NOTHING;    
    }
    if( (((Eevee.time_cur - Eevee.time_staCheck) >= POKEMON_STATE_INTERVAL) &&
              (Eevee.responing == POKEMON_NOTHING)) 
          ||  /* 上面的两个条件同时满足或者下面的条件满足 */
          ((Eevee.state == POKEMON_GOODBYE) && 
              (Eevee.responing != POKEMON_GOODBYE)) )
    {/* 检查状态时间到，如果系统未响应则开始响应状态 */
      Eevee.time_staCheck = Eevee.time_cur;
      check_State();
    }
    

    /* 刷新显示----------------------------------- */
    if (jump < 6) /* 跳跃时每次循环都刷新一次 */
    {
      if(Eevee.satiety > 0) /* 抱食度大于0 才能跳 */
      {  
        if(jump%2 == 0)/* 偶数上跳 */
          Eevee.pos_y = POKEMON_MOVE_UPLINE-25; 
        else/* 奇数下降 */
          Eevee.pos_y = POKEMON_MOVE_UPLINE; 
        jump++;
        refresh_interface(fb_base, fd_lcd, &vinfo);
        Eevee.de_satietyCount++;
      }
      else
      {
        jump = 6;
      }
    }
    else          /* 一般时候隔一次循环刷新一次 */
    {    
      if( refresh )
      { /* 刷新显示 */
        refresh = false;
        refresh_interface(fb_base, fd_lcd, &vinfo);
      }
      else
      {
        refresh = true;
      }
    }

    /* 减少抱食度 */
    if( Eevee.de_satietyCount >= 10 )
    {
      Eevee.de_satietyCount = 0;
      Eevee.satiety = ( Eevee.satiety > 0 ) ? 
                        (Eevee.satiety - 1) : 0;
    }

    printf("saying:%d   responed:%d\n", Eevee.saying, Eevee.state_responed);
    printf("state now :%d  responing:%d\n", Eevee.state, Eevee.responing);
    usleep(10000);
  }
  /* while(1) end--------------------------------------------------------- */

  /* 退场动画 */
  ending_animation(fb_base, fd_lcd, &vinfo);

  /* 恢复字体设置 */
  LCD_SetFont(fonts); 
  LCD_SetColors(TextColor, BackColor, useBack);


  view_block = 0;
  free(tc);
  munmap(fb_base, LCD_BUFF_SIZE*LCD_BLOCK);
  close(fd_lcd);

  printf("out pokemon\n");
  return true;
}


/*******************************************************************************
 * 
 *                              宝可梦APP辅助函数
 * 
*******************************************************************************/

/**
 * @brief 开场动画
 * 
 * @param fb_base 显存映射的基地址
 * @param fd      显示屏的文件描述符
 * @param vinfo   显示屏可变属性的指针
 */
static void opening_animation(int *fb_base, int fd, 
                                  struct fb_var_screeninfo *vinfo)
{
  /* 显示字符相关配置------------------------------- */
  /* 要先把进来前的字体配置记住，在结束后要设置回来 */
  bool useBack;
  u_int32_t TextColor, BackColor;
  sFONT *fonts;
  fonts = LCD_GetFont();
  LCD_GetColors(&TextColor, &BackColor, &useBack);
  LCD_SetFont(&Font32x48); 
  LCD_SetColors(LCD_BLACK, 0, false);


  char path[100] = {0};
  for( int i = 1; i < POKEMON_ANIMA_NUM+1; i++)
  {
    sprintf(path, "%s%d.bmp", POKEMON_ANIMA_PATH, i);
    lcd_show_bmp(LCD_DISPLAY(fb_base, ++view_block), path, 0, 0);
    DispStringLine_EN(LCD_DISPLAY(fb_base, view_block), 8, 2, POKEMON_OPENNING);
    LCD_SET_DISPLAY(fd, vinfo, view_block);
    usleep(20000);
  }


  /* 恢复字体设置 */
  LCD_SetFont(fonts); 
  LCD_SetColors(TextColor, BackColor, useBack);

  sleep(1);
  return;
}                                  

/**
 * @brief 退场动画
 * 
 * @param fb_base 显存映射的基地址
 * @param fd      显示屏的文件描述符
 * @param vinfo   显示屏可变属性的指针
 */
static void ending_animation(int *fb_base, int fd, 
                                  struct fb_var_screeninfo *vinfo)
{
  /* 显示字符相关配置------------------------------- */
  /* 要先把进来前的字体配置记住，在结束后要设置回来 */
  bool useBack;
  u_int32_t TextColor, BackColor;
  sFONT *fonts;
  fonts = LCD_GetFont();
  LCD_GetColors(&TextColor, &BackColor, &useBack);
  LCD_SetFont(&Font32x48); 
  LCD_SetColors(LCD_BLACK, 0, false);

  char path[100] = {0};
  for( int i = POKEMON_ANIMA_NUM; i > 9; i--)
  {
    sprintf(path, "%s%d.bmp", POKEMON_ANIMA_PATH, i);
    lcd_show_bmp(LCD_DISPLAY(fb_base, ++view_block), path, 0, 0);
    DispStringLine_EN(LCD_DISPLAY(fb_base, view_block), 8, 5, POKEMON_ENDING);
    LCD_SET_DISPLAY(fd, vinfo, view_block);
    usleep(70000);
  }


  /* 恢复字体设置 */
  LCD_SetFont(fonts); 
  LCD_SetColors(TextColor, BackColor, useBack);

  sleep(1);
  return;
} 

/**
 * @brief 初始化宝可梦的属性
 * 
 * @param pokemon 要初始化的宝可梦
 */
static void Init_pokemon(myPokemon *pokemon)
{
  pokemon->saying = false;
  pokemon->state_responed = false;
  pokemon->state = POKEMON_MEET;
  pokemon->responing = POKEMON_NOTHING;
  pokemon->time_staCheck = -100;
  pokemon->time_talk = 0;
}

/**
 * @brief 刷新界面内容
 * 
 * @param fb_base 显存映射的基地址
 * @param fd      显示屏的文件描述符
 * @param vinfo   显示屏可变属性的指针
 */
static void refresh_interface(int *fb_base, int fd, 
                                  struct fb_var_screeninfo *vinfo)
{
  char path[100] = {0};
  view_block++;
  /* 清屏 */
  LCD_CLEAN(LCD_DISPLAY(fb_base, view_block));
  /* 先画宝可梦 */
  Eevee.pic_show = (Eevee.pic_show < Eevee.pic_num) ? 
                    Eevee.pic_show + 1 : 1;
  sprintf(path, "%s%d.bmp", MYPOKEMON_PATH, Eevee.pic_show);
  lcd_show_bmp(LCD_DISPLAY(fb_base, view_block), path, 
                  Eevee.pos_x, Eevee.pos_y);
  /* 画背景 */
  lcd_show_bmp_sticker(LCD_DISPLAY(fb_base, view_block),
                        POKEMON_WALLPAPER_PATH, 0, 0);                  
  /* 画空槽 */
  lcd_rectangle(LCD_DISPLAY(fb_base, view_block), 
                  POKEMON_SLOT_X_START, POKEMON_SLOT_Y_START,
                  POKEMON_SLOT_LONG, POKEMON_SLOT_HEIGHT, 
                  POKEMON_SLOT_COLOR);
  /* 抱食度填入空槽 */
  if( Eevee.satiety > POKEMON_HUNGRY )
  { /* 宝可梦不饿抱食度条就是绿色 */
    lcd_rectangle(LCD_DISPLAY(fb_base, view_block), 
                  POKEMON_SLOT_X_START, POKEMON_SLOT_Y_START,
                  Eevee.satiety, POKEMON_SLOT_HEIGHT, LCD_GREEN);
  }
  else
  { /* 宝可梦饥饿抱食度条就是红色 */
    lcd_rectangle(LCD_DISPLAY(fb_base, view_block), 
                  POKEMON_SLOT_X_START, POKEMON_SLOT_Y_START,
                  Eevee.satiety, POKEMON_SLOT_HEIGHT, LCD_RED);
  }

  /* 显示文字 */

  if( !Eevee.state_responed )
  { 
    if( !Eevee.saying ) /* 还没开始说 */
    {
      Eevee.saying = true;  /* 开始说话 */
      Eevee.time_talk = time(NULL) - time_start; /* 开始计时 */
    } 
    switch (Eevee.responing)
    {
    case POKEMON_MEET:
      DispStringLine_EN(LCD_DISPLAY(fb_base, view_block),
                          POKEMON_TEXT_LINE, POKEMON_TEXT_BLOCK, 
                            POKEMON_SAY_MEET);
      break;
    
    case POKEMON_GOODBYE:
      DispStringLine_EN(LCD_DISPLAY(fb_base, view_block),
                          POKEMON_TEXT_LINE, POKEMON_TEXT_BLOCK, 
                            POKEMON_SAY_GOODBEY);
      break;

    case POKEMON_HAPPY:
      DispStringLine_EN(LCD_DISPLAY(fb_base, view_block),
                          POKEMON_TEXT_LINE, POKEMON_TEXT_BLOCK, 
                            POKEMON_SAY_HAPPY);
      break;

    case POKEMON_HUNGER:
      DispStringLine_EN(LCD_DISPLAY(fb_base, view_block),
                          POKEMON_TEXT_LINE, POKEMON_TEXT_BLOCK, 
                            POKEMON_SAY_HUNGER);
      break;

    case POKEMON_EATTING:
      DispStringLine_EN(LCD_DISPLAY(fb_base, view_block),
                          POKEMON_TEXT_LINE, POKEMON_TEXT_BLOCK, 
                            POKEMON_SAY_EATTING);
      break;

    default:

      break;
    }
  }

  LCD_SET_DISPLAY(fd, vinfo, view_block);
}

/**
 * @brief 获取键值
 * 
 * @param x 
 * @param y 
 * @return POKEMON_KEY 
 */
static POKEMON_KEY pokemon_getKey(int x, int y)
{
  /* 点击宝可梦 */
  if( (x >= Eevee.pos_x) && (x <= (Eevee.pos_x+Eevee.width)) 
      && (y >= Eevee.pos_y) && (y <= Eevee.pos_y+Eevee.heigh))
  {
    printf("click pokemon\n");
    return MYPOKEMON_KEY;
  }

  /* 喂食 */
  if( (x >= POKEMON_FOOD_X_START) && (x <= POKEMON_FOOD_X_END) 
      && (y >= POKEMON_FOOD_Y_START) && (y <= POKEMON_FOOD_Y_END))
  {
    printf("click food\n");
    return POKEMON_KEY_FOOD;
  }

  /* 返回键 */
  if( (x >= POKEMON_BACK_X_START) && (x <= POKEMON_BACK_X_END) 
      && (y >= POKEMON_BACK_Y_START) && (y <= POKEMON_BACK_Y_END) )
  {
    printf("click back\n");
    return POKEMON_BACK;
  }

  return POKEMON_KEY_NONE;
}


/**
 * @brief 检查宝可梦状态，如果当前系统没有在响应状态(空闲)，那就响应当前状态
 *        定时调用该函数，定时检查。
 * @param state 输入宝可梦的状态
 */
static void check_State(void)
{
  switch (Eevee.state)
  {
  case POKEMON_NOTHING:
    printf("                nothing!\n");
    
    break;
  
  case POKEMON_MEET:
    printf("                meet!\n");
    Eevee.responing = POKEMON_MEET;
    Eevee.state_responed = false;
    break;

  case POKEMON_GOODBYE:
    printf("                goodbye!\n");
    Eevee.responing = POKEMON_GOODBYE;
    Eevee.state_responed = false;
    break;

  case POKEMON_HAPPY:
    printf("                happy!\n");
    Eevee.responing = POKEMON_HAPPY;
    Eevee.state_responed = false;
    break;
  
  case POKEMON_HUNGER:
    printf("                hunger!\n");
    Eevee.responing = POKEMON_HUNGER;
    Eevee.state_responed = false;
    break;

  case POKEMON_EATTING:
    printf("                eatting!\n");
    Eevee.responing = POKEMON_EATTING;
    Eevee.state_responed = false;
    break;

  default:
    printf("                error!\n");
    break;
  }
}


/*------------------------------ end of file --------------------------------*/

